<template>
  <div v-if="h5">
    <van-field
      v-if="fileList.length > 0 || !isDetail"
      class="m-van-cell"
      :name="item.options && item.options.name"
      :label="item.options && item.options.label"
      :required="item.options && !isDetail && item.options.required"
      :rules="isDetail ? [] : rules"
      :border="false"
      :label-width="item.options?.labelPosition === 'top' ? '100%' : undefined"
      :label-align="(item.options && item.options.labelPosition) || undefined"
      :input-align="(item.options && item.options.inputAlign) || undefined"
      readonly
      autocomplete="off"
    >
      <template #input>
        <van-uploader
          v-model="fileList"
          :max-count="isDetail ? 0 : item.options && item.options.limit"
          :max-size="fileMaxSize"
          :accept="accept"
          :capture="item.options.camera ? 'camera' : undefined"
          :before-read="handleBeforeUpload"
          :after-read="afterRead"
          :before-delete="handleRemove"
          :deletable="!isDetail"
          :disabled="item.options && item.options.disabled"
          :preview-options="{ closeable: true, showIndex: false }"
          class="picture-card"
          result-type="dataUrl"
          @click-preview="onClickPreview"
        >
          <div class="van-uploader__upload">
            <van-icon name="photograph" class="van-uploader__upload-icon" />
            <div class="van-uploader__tip">{{ item.options.tip }}</div>
          </div>

          <template #preview-cover="{ type }">
            <div v-if="!isImageType({ type })" class="preview-cover-file-icon">
              <m-icon :name="getIconByFileType({ type })" />
            </div>
          </template>
        </van-uploader>
      </template>
    </van-field>
  </div>

  <widget-form-item
    v-else
    class="ep-fileupload"
    :designer="designer"
    :widget-list="widgetList"
    :item="item"
    :rules="item.options.camera || isDetail ? [] : rules"
    :index="index"
    :show-message="!isDetail"
  >
    <el-upload
      v-model:file-list="fileList"
      :style="{ width: '100%' }"
      :class="[{ hiddenUploadBtn }]"
      :accept="accept"
      :action="item.options && item.options.action"
      :disabled="(item.options && item.options.disabled) || item.options.camera"
      :limit="item.options && item.options.limit"
      list-type="picture-card"
      :before-upload="handleBeforeUpload"
      :on-success="handleOnSuccess"
      :on-error="handleOnError"
      :on-exceed="handleOnExceed"
      :http-request="handleHttpRequest"
    >
      <el-icon v-if="!isDetail && !hiddenUploadBtn"><Plus /></el-icon>
      <template #file="{ file }">
        <div class="flex-center">
          <img v-if="isImageType(file)" class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
          <div v-else class="ep-preview-cover-file-icon">
            <m-icon :name="getIconByFileType(file)" />
          </div>
          <el-progress
            v-if="file.status === 'uploading'"
            type="circle"
            :width="90"
            :stroke-width="5"
            :percentage="Number(file.percentage || 0)"
            style="display: flex; justify-content: center; align-items: center"
          />
          <span class="el-upload-list__item-actions">
            <span
              v-if="getFileType(file).includes('image/')"
              class="el-upload-list__item-preview"
              @click="handlePictureCardPreview(file)"
            >
              <el-icon><ZoomIn /></el-icon>
            </span>
            <span v-if="isDetail" class="el-upload-list__item-download" @click="downloadFile(file)">
              <el-icon><Download /></el-icon>
            </span>
            <span v-if="!isDetail" class="el-upload-list__item-delete" @click="handleRemove(file)">
              <el-icon><Delete /></el-icon>
            </span>
          </span>
        </div>
      </template>
      <template #tip>
        <div v-if="item.options.tip && !isDetail" class="el-upload__tip">
          {{ item.options.tip }}
        </div>
      </template>
    </el-upload>
  </widget-form-item>
  <template v-if="preview">
    <image-viewer
      v-if="showViewer"
      :teleported="true"
      :initial-index="imageIndex"
      :url-list="previewSrcList"
      @close="closeViewer"
    />
  </template>
</template>

<script>
import _ from 'lodash'
import Compressor from 'compressorjs'
import { ref, computed, watch, getCurrentInstance } from 'vue'
import props from './props'
import field from './field'
import { getFileType, isImageType, getIconByFileType, savePicture } from '../../../utils'
import { showToast } from 'vant'

export default {
  name: 'MFileupload',
  props,
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    const { proxy } = getCurrentInstance()
    const showViewer = ref(false)

    /** 预览图片 */
    const preview = computed(() => {
      return Array.isArray(fileList.value) && fileList.value.length > 0
    })

    /** 预览图片索引位置 */
    const previewIndex = ref(0)
    const imageIndex = computed(() => {
      if (previewIndex.value > fileList.value.length - 1) {
        return 0
      }
      return previewIndex.value
    })
    /** 预览图片列表 */
    const previewSrcList = computed(() => {
      return fileList.value.map((s) => s.url)
    })
    const prevOverflow = ''

    function closeViewer() {
      document.body.style.overflow = prevOverflow
      showViewer.value = false
    }

    /** 移动端部分IOS不支持accept格式问题 */
    const accept = computed(() => {
      var ua = navigator.userAgent.toLowerCase()
      if (ua.indexOf('iphone') !== -1) {
        return ''
      }
      // 如果是安卓手机，必须返回 image/*，否则无法打开相机
      return 'image/*'
    })

    /** 上传文件列表 */
    const fileList = ref([])

    /** 格式化文件列表存储对象 */
    const getFileList = computed(() => {
      const list = fileList.value.map((m) => {
        const { id, name, type, size } = m
        return { id, name, type, size }
      })
      console.log('getFileList :>> ', list)
      return list
    })

    /** 设置上传文件列表 */
    const setFileList = (files) => {
      const addfiles = _.cloneDeep(files.filter((m) => !fileList.value.find((x) => x.id === m.id)))
      const { actionBlob } = props.item.options
      if (actionBlob) {
        addfiles.forEach((file) => {
          if (file.id && file.type.includes('image/')) {
            file.url = proxy.$axios.getUrl(actionBlob + '/' + file.id)
            file.isImage = true
          }
        })
      }
      fileList.value.push(...addfiles)
      console.log('fileList.value :>> ', fileList.value)
    }

    /** PC端下载文件，照片采用downloadByBlob原下载方式 */
    const downloadFile = (file) => {
      const { actionBlob } = props.item.options
      proxy.$axios.download(actionBlob + '/' + file.id)
    }

    watch(
      () => props.modelValue,
      (files) => {
        if (files) {
          setFileList(files)
        }
      },
      { immediate: true }
    )

    // setTimeout(() => {
    //   emit('update:modelValue', [
    //     {
    //       id: '3a0f8daf-7273-da67-b8cc-056ad1c562e9',
    //       name: 'food2.jpeg',
    //       type: 'image/jpeg',
    //       size: 400960
    //     }, {
    //       id: '3a0f116a-95ce-35eb-0f87-d0f3d3224ab1',
    //       name: 'food3.jpeg',
    //       type: 'image/jpeg',
    //       size: 400960
    //     }
    //   ])
    // }, 3000)

    /** 移除图片 */
    const handleRemove = (file) => {
      const { isDetail } = field(props, emit)
      if (isDetail.value) {
        return false
      }
      fileList.value.splice(fileList.value.indexOf(file), 1)
      emit('update:modelValue', getFileList.value)
    }
    /** PC预览图片 */
    const handlePictureCardPreview = (file) => {
      if (!preview.value) return
      const index = fileList.value.findIndex((x) => x === file)
      previewIndex.value = index
      document.body.style.overflow = 'hidden'
      showViewer.value = true
    }

    const fileMaxSize = computed(() => {
      return Number(props.item.options.fileMaxSize || 5) * 1024 * 1024
    })

    /** 上传文件之前的钩子 */
    const handleBeforeUpload = (file) =>
      new Promise((resolve, reject) => {
        /** 限制文件类型 */
        const accept = props.item.options.accept || '*'
        if (accept !== '*') {
          const extArrs = accept.replace('，', ',').split(',')
          const extFileName = file.name.substring(file.name.lastIndexOf('.'))
          if (!extArrs.includes(extFileName)) {
            if (props.h5) {
              showToast(`不支持格式：${extFileName}`)
            } else {
              proxy.$message.error(`不支持格式：${extFileName}`)
            }
            reject()
          }
        }

        /** 判断文件大小 */
        const fileMaxSize = props.item.options.fileMaxSize || 5
        if (file.size >= Number(fileMaxSize) * 1024 * 1024) {
          if (props.h5) {
            showToast(`文件大小已超出：${fileMaxSize}MB`)
          } else {
            proxy.$message.error(`文件大小已超出：${fileMaxSize}MB`)
          }
          reject()
        }
        if (props.item.options.reduce && file.type.includes('image')) {
          // 会将图片修正为正确方向
          new Compressor(file, {
            convertSize: 2000000, // 超出大小压缩
            maxWidth: props.item.options.reduce === 2 ? 1440 : 3024, // 输出图像最大宽度
            quality: props.item.options.reduce === 2 ? 0.8 : 0.9, // 压缩质量
            success: resolve,
            error(err) {
              console.log(err.message)
              if (props.h5) {
                showToast('图片压缩错误！')
              } else {
                proxy.$message.error('图片压缩错误！')
              }
              reject()
            }
          })
        } else {
          resolve()
        }
      })

    /** 文件上传成功时的钩子 */
    const handleOnSuccess = (response, uploadFile, uploadFiles) => {
      // 回写id
      const { id } = response
      if (id) uploadFile.id = id
      uploadFile.type = uploadFile.raw.type
      console.log('uploadFile :>> ', uploadFile)

      const list = uploadFiles.map((m) => {
        const { id, name, type, size } = m
        return { id, name, type, size }
      })
      emit('update:modelValue', list)
    }
    /** 文件上传失败时的钩子 */
    const handleOnError = () => {
      proxy.$message.error('上传失败！')
    }
    /** 当超出限制时，执行的钩子函数 */
    const handleOnExceed = () => {
      proxy.$message.error(`最大上传数量(${props.item.options.limit})已超出.`)
    }
    /** 覆盖默认的 Xhr 行为 */
    const handleHttpRequest = (options) => {
      console.log('options :>> ', options, fileList)
      const file = fileList.value.find((m) => m.uid === options.file.uid)
      file.status = 'uploading'
      file.size = options.file.size

      const config = {
        // axios 1.3.4 更新
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        'Content-Type': 'multipart/form-data',
        timeout: 999999,
        onUploadProgress: (progressEvent) => {
          file.percentage = Number(((progressEvent.loaded / progressEvent.total) * 100).toFixed(2))
        }
      }

      const formdata = new FormData()
      formdata.append('file', options.file)

      proxy.$axios
        .post(options.action, formdata, config)
        .then((res) => {
          file.progress = 'success'
          options.onSuccess(res)
        })
        .catch((error) => {
          console.log('error :>> ', error)
          file.progress = 'warning'
          options.onError(error)
        })
    }

    /** 移动端文件读取完成后的回调函数 */
    const afterRead = (content) => {
      console.log('content :>> ', content)
      // content.uid = new Date().getTime()
      content.name = content.file.name
      content.type = content.file.type
      content.size = content.file.size
      content.status = 'uploading'
      content.message = '上传中...'

      const config = {
        // axios 1.3.4 更新
        headers: {
          'Content-Type': 'multipart/form-data'
        },
        'Content-Type': 'multipart/form-data',
        timeout: 999999
      }

      const formdata = new FormData()
      formdata.append('file', content.file)

      proxy.$axios
        .post(props.item.options.action, formdata, config)
        .then((res) => {
          content.id = res.id
          content.status = 'done'
          content.message = ''
          emit('update:modelValue', getFileList.value)
        })
        .catch((error) => {
          console.log('error :>> ', error)
          handleRemove(content)
          showToast('上传失败！')
        })
    }

    /** 超出上传文件数量时，隐藏上传按钮 */
    const hiddenUploadBtn = computed(() => {
      if (props.item.options.camera) {
        return true
      }
      return fileList.value.length >= props.item.options.limit
    })

    /** 移动端预览点击文件 */
    const onClickPreview = (file) => {
      if (!file.isImage) {
        const { actionBlob } = props.item.options
        const remoteUrl = proxy.$axios.getUrl(actionBlob + '/' + file.id)
        const extFileName = file.name.substring(file.name.lastIndexOf('.') + 1)
        const params = {
          fileName: file.uid || file.id, // 文件名
          fileType: extFileName, // 文件类型  doc, docx, xls, xlsx, ppt, pptx, pdf
          url: location.origin + remoteUrl // 下载资源的 url，完整的url地址 https://xxxxxx.xx
        }
        if (window.ECampus) {
          window.ECampus.downloadFile(params)
        } else {
          proxy.$axios.download(actionBlob + '/' + file.id)
        }
      }
    }

    return {
      preview,
      imageIndex,
      showViewer,
      previewSrcList,
      fileList,
      accept,
      hiddenUploadBtn,
      fileMaxSize,
      afterRead,
      getFileType,
      isImageType,
      getIconByFileType,
      downloadFile,
      savePicture,
      closeViewer,
      handleRemove,
      handlePictureCardPreview,
      handleBeforeUpload,
      handleOnSuccess,
      handleOnError,
      handleOnExceed,
      handleHttpRequest,
      onClickPreview,
      ...field(props, emit)
    }
  }
}
</script>

<style lang="scss" scoped>
.flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-grow: 1;
}

.ep-fileupload {
  .ep-preview-cover-file-icon {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    font-size: 45px;
  }
  .el-upload-list--picture {
    .ep-preview-cover-file-icon {
      font-size: 30px;
    }
    .el-upload-list__item-actions {
      position: absolute;
      width: 100%;
      height: 100%;
      left: 0;
      top: 0;
      cursor: default;
      display: inline-flex;
      justify-content: flex-start;
      align-items: center;
      color: #fff;
      opacity: 0;
      font-size: 20px;
      background-color: rgba(0, 0, 0, 0.04);
      transition: opacity var(--el-transition-duration);

      &:hover {
        opacity: 1;
      }
    }
    .el-upload-list__item-actions:hover span {
      display: inline-flex;
    }

    .el-upload-list__item-name {
      line-height: 16px;
    }

    .el-upload-list__item-thumbnail {
      margin: 0;
      width: 30px;
      height: 30px;
      z-index: auto;
    }

    .el-upload-list__item-actions span {
      display: none;
      cursor: pointer;
      &.el-upload-list__item-preview {
        width: 50%;
        padding-left: 16px;
      }

      &.el-upload-list__item-action {
        color: var(--el-text-color-regular);
        border-radius: 2px;

        &:hover {
          background-color: rgba(0, 0, 0, 0.05);
        }
      }

      .action-icon {
        margin: 5px;
      }
    }

    .el-icon--close {
      z-index: 1;
    }
  }

  .el-upload__tip {
    font-size: 12px;
    color: var(--el-color-info);
    margin-top: 7px;
  }

  :deep(.hiddenUploadBtn .el-upload-list .el-upload--picture-card) {
    display: none !important;
  }

  :deep(.el-upload-list--picture-card) {
    .el-upload-list__item {
      width: 100px !important;
      height: 100px !important;
    }
    .el-upload--picture-card {
      width: 100px !important;
      height: 100px !important;
    }

    .el-upload-list__item-thumbnail {
      height: auto !important;
    }
  }

  :deep(.el-progress__text) {
    color: #fff;
  }
}

.van-uploader {
  &.picture {
    .van-uploader__upload {
      width: 78px;
      height: 78px;
      border-radius: 10px;
    }
    :deep(.van-uploader__file) {
      width: 78px;
      height: 78px;
      border-radius: 10px;
    }
    :deep(.van-uploader__preview-image) {
      width: 78px;
      height: 78px;
      border-radius: 10px;
    }
    :deep(.van-uploader__preview:nth-child(4n)) {
      margin-right: 0;
    }
    :deep(.van-uploader__preview-delete--shadow) {
      border-radius: 0 10px 0 8px;
      width: 18px;
      height: 16px;
      background: rgba(0, 0, 0, 0.5);
    }
    :deep(.van-uploader__preview-delete-icon) {
      top: 1px;
      right: 3px;
    }
  }

  &.picture-card {
    position: relative;
    .van-uploader__upload {
      width: 101px;
      height: 101px;
      border-radius: 10px;
    }
    :deep(.van-uploader__file) {
      width: 101px;
      height: 101px;
      border-radius: 10px;
    }
    :deep(.van-uploader__preview-image) {
      width: 101px;
      height: 101px;
      border-radius: 10px;
    }
    :deep(.van-uploader__preview:nth-child(3n)) {
      margin-right: 0;
    }
    :deep(.van-uploader__preview-delete--shadow) {
      border-radius: 0 10px 0 8px;
      width: 20px;
      height: 16px;
    }
    :deep(.van-uploader__preview-delete-icon) {
      top: 1px;
      right: 4px;
    }
  }
  .file-btn {
    min-width: 300px;
  }
  .van-uploader__tip {
    font-size: var(--van-font-size-xs);
    color: var(--van-gray-6);
  }
  .preview-cover-file-icon {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    background: var(--van-uploader-file-background);
    font-size: 45px;
  }
}

/** 处理左侧文本对齐方式 */
.van-field--label-left {
  .van-uploader .file-btn {
    min-width: 200px;
  }
  .van-uploader {
    &.picture {
      :deep(.van-uploader__preview:nth-child(3n)) {
        margin-right: 0;
      }
      :deep(.van-uploader__preview:nth-child(4n)) {
        margin-right: var(--van-padding-xs);
      }
    }
    &.picture-card {
      :deep(.van-uploader__preview:nth-child(2n)) {
        margin-right: 0;
      }
      :deep(.van-uploader__preview:nth-child(3n)) {
        margin-right: var(--van-padding-xs);
      }
    }
  }
}
</style>

<style lang="scss">
/** 详情样式处理 */
.render-form-container .detail {
  .el-upload--picture-card {
    display: none !important;
  }
  .el-upload--picture {
    display: none !important;
  }
}
</style>
